/**
 * 公共函数
 */

package main

import (
    "fmt"
    "os"
    "log"
    "time"
    "sync"
    "regexp"
    "unsafe"
    "strconv"
    "strings"
    // "runtime"
    "runtime/debug"
    "path/filepath"
    "encoding/json"
    "gitee.com/mayiweb/gohs"
    "github.com/axgle/mahonia"
)

var (
    Lock sync.Mutex
)


// 获得交易所名称
func GetExchangeName(ExchangeID string) string {
    var title string

    switch ExchangeID {
        case "1":
            title = "上海证券交易所"

        case "2":
            title = "深圳证券交易所"

        case "F1":
            title = "郑州商品交易所"

        case "F2":
            title = "大连商品交易所"

        case "F3":
            title = "上海期货交易所"

        case "F4":
            title = "中国金融期货交易所"

        case "F5":
            title = "上海国际能源交易中心"

        case "F6":
            title = "广州期货交易所"

        default:
            title = "未知"
    }

    return title
}

// 获得报单买卖方向
func GetDirectionTitle(Direction string) string {
    var title string

    switch Direction {
        case "1":
            title = "买"

        case "2":
            title = "卖"

        case "F":
            title = "出借"

        case "G":
            title = "借入"

        default:
            title = "未知"
    }

    return title
}

// 获得开平标志
func GetOffsetFlagTitle(OffsetFlag string) string {

    title := ""

    switch OffsetFlag {

        case "1":
            title = "开仓"

        case "2":
            title = "平仓"

        case "3":
            title = "交割"

        case "4":
            title = "平今"

        case "6":
            title = "组合单边平仓"

        default:
            title = "未知"
    }

    return title
}

// 获得报单状态
func GetOrderStatusTitle(OrderStatus string) string {

    title := ""

    switch OrderStatus {
        case "0":
            title = "未报"

        case "1":
            title = "待报"

        case "2":
            title = "已报"

        case "3":
            title = "已报待撤"

        case "4":
            title = "部成待撤"

        case "5":
            title = "部撤"

        case "6":
            title = "已撤"

        case "7":
            title = "部成"

        case "8":
            title = "已成"

        case "9":
            title = "废单"

        case "D":
            title = "撤废"

        case "U":
            title = "确认待撤"

        case "V":
            title = "已确认"

        case "W":
            title = "待确认"

        default:
            title = "未知状态"
    }

    return title
}

// 获得投机套保类型
func GetHedgeTypeTitle(HedgeType string) string {

    title := ""

    switch HedgeType {

        case "0":
            title = "投机"

        case "1":
            title = "套保"

        case "2":
            title = "套利"

        case "3":
            title = "做市商"

        case "4":
            title = "备兑"

        default:
            title = "未知"
    }

    return title
}

// 获得产品类型
func GetProductTypeTitle(ProductType string) string {

    title := ""

    switch ProductType {

        case "1":
            title = "期货"

        case "2":
            title = "期货期权"

        case "3":
            title = "组合"

        case "4":
            title = "即期"

        case "5":
            title = "期转现"

        case "6":
            title = "证券"

        case "7":
            title = "股票期权"

        default:
            title = "未知"
    }

    return title
}

// 是否空指针
func IsNullPointer(p interface{}) bool {

    if (p == nil) {
        return true
    }

    pv := Sprintf("%v", p)
    if pv == "0" {
        return true
    }

    return false
}

// 退出程序
func Exit() {
    os.Exit(1)
}

// 获得当前时间
func GetCurrentTime() string {
    return string(time.Now().Format("2006-01-02 15:04:05"))
}

// 获得当前时间戳
func GetCurrentUnix() int {
    return int(time.Now().Unix())
}

// 日期转时间（例： 2020-06-29 08:00:00 转为 08:00:00）
func DateToTime(date string) string {
    loc, _      := time.LoadLocation("Local")
    theTime1, _ := time.ParseInLocation("2006-01-02 15:04:05", date, loc)

    return time.Unix(theTime1.Unix(), 0).Format("15:04:05")
}

/**
 * 时间转时间戳
 *
 * 例：TimeToUnix("09:30:00")
 */
func TimeToUnix(strTime string) int {

    // 如果就写了个时间没写日期，视为当天日期
    if len(strTime) == 8 {
        strTime = time.Now().Format("2006-01-02") + " " + strTime
    }

    loc, _      := time.LoadLocation("Local")    // 获取时区
    theTime, _ := time.ParseInLocation("2006-01-02 15:04:05", strTime, loc)

    return int(theTime.Unix())
}

// 请求日志
func ReqMsg(Msg string) {
    log.Println(Msg)
}

// 请求 api 出现错误
func ReqFailMsg(Msg string, iResult int) {
    fmt.Printf("%v [%d: %s]\n", Msg, iResult, iResultMsg(iResult));
}

// 请求失败的错误码对应消息
func iResultMsg(iResult int) (string) {

    msg := ""

    switch (iResult) {
        case 0:
            msg = "成功";
            break;

        case -1:
            msg = "请检查账号是否已经登陆";
            break;

        case -2:
            msg = "未处理请求超过许可数";
            break;

        case -3:
            msg = "每秒发送请求数超过许可数";
            break;

        default:
            msg = "未知错误";
            break;
    }

    return msg;
}

// 打印错误（存在错误的时候）
func PrintErr(err error) {
    if err != nil {
        Println(err)
    }
}

// 检查错误，有就抛出 panic
func CheckErr(err error) {
    if err != nil {
        panic(err)
    }
}

// 处理 panic
func CheckPanic() {
    if err := recover(); err != nil {

        ErrStr := "-------------------------------------------------------------------------------------------------\n" +
                "- 错误提示\n" +
                "- 出错时间：" + GetCurrentTime() + "\n" +
                "- 错误内容：" + fmt.Sprintf("%s", err) + "\n\n" +
                string(debug.Stack()) +
                "-------------------------------------------------------------------------------------------------"

        Println(ErrStr)
    }
}

// 判断文件或目录是否存在
func PathExists(path string) (bool, error) {
    _, err := os.Stat(path)
    if err == nil {
        return true, nil
    }

    if os.IsNotExist(err) {
        return false, nil
    }

    return false, err
}

// float64 保留几位小数点
func Decimal(f float64, n int) float64 {

    if f == 1.7976931348623157e+308 {
        f = 0.0000
    }

    value, _ := strconv.ParseFloat(fmt.Sprintf("%." + strconv.Itoa(n) + "f", f), 64)
    return value
}

// int 转 string
func IntToString(i int) string {
    return strconv.Itoa(i)
}

// int32 转 string
func Int32ToString(n int32) string {
    buf := [11]byte{}
    pos := len(buf)
    i := int64(n)
    signed := i < 0
    if signed {
        i = -i
    }
    for {
        pos--
        buf[pos], i = '0'+byte(i%10), i/10
        if i == 0 {
            if signed {
                pos--
                buf[pos] = '-'
            }
            return string(buf[pos:])
        }
    }
}

// float64 转 string
func Float64ToString(f float64) string {
    return strconv.FormatFloat(f, 'f', -1, 64)
}

// string 转 float64
func StringToFloat64(str string) float64 {
    f64, _ := strconv.ParseFloat(str, 64)
    return f64
}

// string 转 int
func StringToInt(str string) int {
    num, err := strconv.Atoi(str)
    if err != nil {
        return 0
    } else {
        return num
    }
}

// string 转 int32
func StringToInt32(str string) int32 {
    num, err := strconv.ParseInt(str, 10, 32)
    if err != nil {
        num = 0
    }
    return int32(num)
}

// 去掉左右两边空格
func TrimSpace (str string) string {
    return strings.TrimSpace(str)
}

// fmt.Println
func Println(a ...interface{}) (n int, err error) {
    return fmt.Println(a...)
}

// fmt.Sprintf
func Sprintf(format string, a ...interface{}) string {
    return fmt.Sprintf(format, a...)
}

// fmt.Printf
func Printf(format string, a ...interface{}) (n int, err error) {
    return fmt.Printf(format, a...)
}

// log.Println
func LogPrintln(a ...interface{}) {
    log.Println(a...)
}

// log.Printf
func LogPrintf(format string, a ...interface{}) {
    log.Printf(format, a...)
}

func StrInArray(str string, arr []string) bool {
    for _, v := range arr {
        if str == v {
            return true
        }
    }
    return false
}

/**
 * 编码转换
 *
 * 例： result = ConvertToString(text, "gbk", "utf-8")
 */
func ConvertToString(text string, srcCode string, tagCode string) string {

    srcCoder  := mahonia.NewDecoder(srcCode)
    tagCoder  := mahonia.NewDecoder(tagCode)
    srcResult := srcCoder.ConvertString(text)

    _, cdata, _ := tagCoder.Translate([]byte(srcResult), true)

    result := string(cdata)

    return result
}

// 编码转换（gbk 转 utf8）
func GbkToUtf8(text string) string {

    srcCoder  := mahonia.NewDecoder("gbk")
    tagCoder  := mahonia.NewDecoder("utf-8")
    srcResult := srcCoder.ConvertString(text)

    _, cdata, _ := tagCoder.Translate([]byte(srcResult), true)

    result := string(cdata)

    return result
}

// 获得合约名称（如没有则返回传入的合约代码）
func GetInstrumentName(InstrumentID string) string {
    Instrument, InstrumentOk := GetInstrumentInfo(InstrumentID)
    if !InstrumentOk {
        return InstrumentID
    }

    return Instrument.InstrumentName
}

// 获得期权对应的合约
func GetOptionInstrumentID(InstrumentID string) string {

    // 如果 map 有记录则使用已存在的记录
    return regexp.MustCompile("^[a-zA-Z]+\\d+").FindString(InstrumentID)
}

// 获取当前路径
func GetCurrentDirectory() string {
    dir, err := filepath.Abs(filepath.Dir(os.Args[0]))  //返回绝对路径  filepath.Dir(os.Args[0])去除最后一个元素的路径
    if err != nil {
        log.Fatal(err)
    }
    return strings.Replace(dir, "\\", "/", -1) //将\替换成/
}

func JsonEncode(str interface{}) string {
    jsonBytes, err := json.Marshal(str)
    if err != nil {
        fmt.Println(err)
    }

    return string(jsonBytes)
}

// 2个日期差异日列表（将差异日列出来）
func DateDiffDayList(startDate, endDate string) []string {

    t1, _ := time.Parse("2006-01-02", startDate)
    t2, _ := time.Parse("2006-01-02", endDate)

    DateDiffDays := int(t2.Sub(t1).Hours() / 24)

    dd, _ := time.ParseDuration("24h")

    var DaysList []string

    for i := 1; i <= DateDiffDays; i++ {
        DaysList = append(DaysList, t1.Add(dd * time.Duration(i)).Format("2006-01-02"))
    }

    return DaysList
}

// 计算2个日期之间相距的天数
func DateDiffDay(startDate, endDate string) int {
    t1, _ := time.Parse("20060102", startDate)
    t2, _ := time.Parse("20060102", endDate)

    return int(t2.Sub(t1).Hours() / 24)
}

// 正则替换
func RegexpRreplace(reg string, str string, strTo string) string {
    r, _ := regexp.Compile(reg)
    return r.ReplaceAllString(str, strTo)
}

// 休眠（毫秒，1000 毫秒等于 1 秒）
func Sleep(number int) {
    time.Sleep(time.Millisecond * time.Duration(number))
}

// 获得 swig 中的 int32 类型字段（将  C++ 中的 int32 转为 go 中的 Int32，类型标识 SwigcptrInt32_t）
func GetSwigInt32(SwigCptr gohs.Int32_t) int32 {
    numPtr := SwigCptr.Swigcptr()
    num    := *(*int32)(unsafe.Pointer(numPtr))

    gohs.Swig_free(numPtr)

    return num
}

// 设置 swig 中的 int32 类型字段
func SetSwigInt32(num int32) gohs.Int32_t {
    return gohs.SwigcptrInt32_t(uintptr(unsafe.Pointer(&num)))
}

// 恒生中的时间处理
func HsTime(time string) string {

    // 有些时间后面还带3位微秒
    if len(time) > 6 {
        time = time[:len(time)-3]
    }

    // 时间长度不对，补位
    if len(time) < 6 {
        time = Sprintf("%06v", time)
    }

    timeStr := Sprintf("%v:%v:%v", time[0:2], time[2:4], time[4:6])

    return timeStr
}

// 获得正数的涨跌幅度（传入负数会转为正数）
func GetFudu(f64 float64) float64 {

    if f64 < 0 {
        return f64 * -1
    }

    return f64
}

// 检查订单申报（限制：每秒不能超过5笔）
func CheckOrderDeclare() {

    Lock.Lock()
    defer Lock.Unlock()

    // 时间戳
    timeStamp := time.Now().Unix()

    count := 1

    mOrderDeclareVal, mOrderDeclareOk := MapOrderDeclare.Get(timeStamp)
    if mOrderDeclareOk {
        count = mOrderDeclareVal.(int)

        if count >= 4 {
            Sleep(1000)
        }

        count += 1
    }

    NewTimeStamp := time.Now().Unix()
    if timeStamp != NewTimeStamp {
        count = 1
    }

    MapOrderDeclare.Set(NewTimeStamp, count)
}

/**
 * 字符串后面补位空格
 *
 * 例： ConvertToString("abc", 6)  // 字符串长6位，不足的补空格
 */
func StrAfterSpace(str string, length int) string {
    result := str

    for i := len(str); i < length; i++ {
        result += " "
    }

    return result
}